/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include "ScanMethod6FNX.h"

//--------------------------------------------------------------------

ScanMethod6FNX::ScanMethod6FNX(void)
{
	myType = __wmtNone;
}

ScanMethod6FNX::~ScanMethod6FNX(void)
{
}

//--------------------------------------------------------------------

void ScanMethod6FNX::SetType(WorkMethodType typeIn)
{
	myType = typeIn;
}

//--------------------------------------------------------------------

int ScanMethod6FNX::InternalInit(void)
{
	int res;

	if(myType==__wmtNone)
		return MSR_ERR_PARAMETER;

	res = BuildPortList(true);
	if(res!=MSR_ERR_OK)
		return res;
	
	FillRandomBuffer();
	
	if(workPhase==__wptDiscovery) return MSR_ERR_OK;
	if(workPhase==__wptScan)      return MSR_ERR_OK;

	return MSR_ERR_PHASE;
}

//--------------------------------------------------------------------

int ScanMethod6FNX::InternalExecute(void)
{
	ScanPacket* pack;

	if(addrList==NULL) return MSR_ERR_PARAMETER;

	ClearRespReceivedFlag();

	ScanPair sp;
	while(GetNextPair(&sp, __sptIpAndPort) && !stopStopStop){
		if(PacketsUsed()>0){
			CheckResponses();
			CheckDeaths();
		}

		if(sp.addr!=NULL){
			if(!sp.addr->addrIPv6.IsEmpty()){
				if(workPhase==__wptDiscovery && sp.addr->alive){
					// it's the discovery phase and a previous discovery method has found it already
					// so skip this one
				}
				else{
					if(config->xMinDelay>0){
						if(lastScanTime!=0){
							waitDone = false;
							while(!waitDone){
								CheckResponses();
								SleepEx(1, TRUE);
								scanTime = GetTickCount();
								scanTime = ComputeDeltaTime(lastScanTime, scanTime);
								if(scanTime>=config->xMinDelay)
									waitDone = true;
							}
						}
						lastScanTime = GetTickCount();
					}

					pack = FindFirstUnusedPacket();
					while(pack==NULL){
						SleepEx(waitTime, TRUE);
						CheckResponses();
						CheckDeaths();
						pack = FindFirstUnusedPacket();
					}

					if(pack!=NULL){
						pack->Initialize(true);
						pack->sTime       = GetTickCount();
						sp.addr->addrIPv6.Get(&pack->addr6);
						pack->port        = sp.port;
						pack->methodType  = myType;
						pack->fullAddress = sp.addr;
						pack->machineInfo = FindMachineByIp6(&(pack->addr6));

						if(!SendFNX(sp.addr, sp.port, pack))
							pack->Initialize(false);

						if((packSent % notifTcp) == 0 && packSent>0){
							if(hWnd!=NULL)
								PostMessage(hWnd, CHS_MSG_PACKETS_SENT, 0, (LPARAM)packSent);
						}
					}
				}
			}
		}
	}

	// wait until the end
	while(PacketsUsed()>0){
		CheckResponses();
		CheckDeaths();
		SleepEx(1, TRUE);
	}

	CheckRespReceivedFlag();

	return MSR_ERR_OK;
}

//--------------------------------------------------------------------

void ScanMethod6FNX::ComputeRandomValues(void)
{
	rndTcpSeq = GetRandomValue();
	rndTcpAck = GetRandomValue();
}

bool ScanMethod6FNX::SendFNX(MFullAddress* fullAddrIn, WORD portIn, ScanPacket* packIn)
{
	BYTE flags;
	BYTE srcMAC[6];
	in_addr6 srcIPv6;
	bool res;

	if(fullAddrIn==NULL || packIn==NULL){
		// nothing to send
		return false;
	}

	switch(myType){
		case __wmt6Fin :
			flags = TH_FIN;
			break;
		case __wmt6Null :
			flags = 0x00;
			break;
		case __wmt6Xmas :
			flags = TH_URG | TH_PUSH | TH_FIN;
			break;
		default :
			flags = 0xFF;
			break;

	}
	if(flags==0xFF) return false;

	ComputeRandomValues();

	winPcap->FillSrcMAC(&srcMAC[0]);
	winPcap->GetIPv6UnicastAddress(&srcIPv6);

	res = packetBuilderEx6->BuildPackTcpFlat(
		&srcIPv6,
		&(packIn->addr6),
		portIn,
		rndTcpSeq,
		rndTcpAck,
		flags);
	if(!res) return false;

	if(!packetBuilderEx6->AddEthernetHeader(srcMAC, fullAddrIn->addrMAC.addr))
		return false;

	return SendPacket(packetBuilderEx6->GetPacket(), (int)packetBuilderEx6->GetPacketSize());
}

//--------------------------------------------------------------------

void ScanMethod6FNX::ProcessTcp6(BYTE* dataIn, int sizeIn, BYTE* srcIP, BYTE* srcMAC)
{
	TCPHeaderPG  *tcph;
	ScanPacket   *pack;
	in_addr6     ia6;
	MFullAddress *addr;
	MMachineInfo *mach;

	if(sizeIn<sizeof(TCPHeaderPG)) return;

	tcph = (TCPHeaderPG*)dataIn;

	if(!(tcph->Flags & TH_RST)){
		// this is not what I am expecting, skip
		return;
	}

	memcpy(ia6.u.Byte, srcIP, 16);

	pack = FindPacket6(&ia6, ntohs(tcph->SourcePort), myType);
	if(pack==NULL) return;

	if(workPhase==__wptDiscovery){
		addr = pack->fullAddress;
		if(addr!=NULL)
			addr->alive = true;
	}
	else{
		mach = pack->machineInfo;
		if(mach!=NULL){
			mach->address.alive = true;
			mach->address.respReceived = true;
		}
	}

	pack->Initialize(false);
}

//---------------------------------------------------------------------------

int ScanMethod6FNX::InternalDeath(ScanPacket* spIn)
{
	MMachineInfo *machineInfo;
	MPortInfo mpi;

	if(workPhase!=__wptDiscovery){
		machineInfo = spIn->machineInfo;
		if(machineInfo!=NULL){
			mpi.port         = spIn->port;
			mpi.method       = myType;
			mpi.timeInterval = 0;
			mpi.dataSize     = 0;
			machineInfo->AddPortInfo(&mpi);
		}
	}

	return MSR_ERR_OK;
}

//---------------------------------------------------------------------------
